#include "applicationsmanager.h"

#include <optra/common/proxy.h>
#include <optra/common/db.h>
#include <QSqlTableModel>
#include <QSqlDatabase>

#include "optrzone.h"
#include <QFile>
#include <QLibrary>
#include <QUuid>

#include "zonesmanager.h"

O_Z

#define optra() OZ

        ApplicationsManager* appmanager;
extern ZonesManager* zmanager;

typedef int (*Prototype2)(Optra::AppOptraInfo*);
ApplicationsManager::ApplicationsManager(QObject *parent) :
        QObject(parent)
{
    appmanager = this;
}

int ApplicationsManager::applicationTypeId(const QString& typen)
{
    QSqlTableModel model(0, OZ->db()->db());
    model.setTable("applicationstype");
    model.setFilter(QString("name='%1'").arg(typen));
    model.select();
    if (model.rowCount() > 0){
        return model.record(0).value("id").toInt();
    }

    return 0;
}

bool ApplicationsManager::setDBDependency(const QStringList& sl, int sid, const QString& depname)
{
    QSqlQuery query = optra()->db()->query();
    int typeidcri = zmanager->zoneDependency(depname);

    bool ret = true;
    QVariantList tidl;
    QVariantList sidl;
    QVariantList typeId;
    QVariantList tuniq;

    foreach(QString dep, sl){
        sidl << sid;
        int id = 0;
        ZoneInternalInfo zi;
        zi.unique = dep;
        if (zmanager->zoneInstaled(zi, &id)){
            tidl << id;
        }else{
            tidl << 0;
            ret = false;
        }
        tuniq << dep;
        typeId << typeidcri;
    }

    query.prepare("INSERT INTO applicationsdep (sid, tid, typeid, tunique) VALUES (?, ?, ?, ?)");
    query.addBindValue(sidl);
    query.addBindValue(tidl);
    query.addBindValue(typeId);
    query.addBindValue(tuniq);

    if (!query.execBatch()){
        qDebug() << query.lastError();
        return false;
    }
    return ret;
}

bool ApplicationsManager::applicationInfo(QFile* file, Optra::AppOptraInfo* info)
{
    Q_ASSERT(info);
    QLibrary lib(file->fileName());
    if (!lib.load()){
        optra()->warning(QString("Can't Load application:%1").arg(lib.fileName()));
        return false;
    }

    Prototype2 entry = (Prototype2)lib.resolve("entry_point2");
    if (entry == NULL){
        optra()->warning(QString("Can't Find Entry point in:%1").arg(lib.fileName()));
        return false;
    }

    if (entry(info) != 0){
        optra()->warning(QString("error in application init:%1").arg(lib.fileName()));
        return false;
    }

    return true;
}

bool ApplicationsManager::installApplication(QFile* file)
{
    if (file == NULL){
        return false;
    }

    if (!file->isOpen()){
        return false;
    }

    Optra::AppOptraInfo aoi;
    if (!applicationInfo(file, &aoi)){
        optra()->warning(QString("Application:%1 loaded failed(unique:%2)").arg(aoi.name()).arg(aoi.unique()));
        return false;
    }

    optra()->debug(QString("Application:%1 loaded(unique:%2)").arg(aoi.name()).arg(aoi.unique()));

    int atid = applicationTypeId("Optra");
    QSqlQuery query = optra()->db()->query();
    query.prepare("INSERT INTO applications (path, guid, displayname, uni, typeid) VALUES (:path, :guid, :display, :unique, :typeid)");
    query.bindValue(":path", file->fileName());
    query.bindValue(":quid", QUuid::createUuid().toString());
    query.bindValue(":display", aoi.name());
    query.bindValue(":unique", aoi.unique());
    query.bindValue(":typeid", atid);


    optra()->db()->db().transaction();

    if (!query.exec()){
        optra()->warning(QString("error in insert data %1").arg(query.lastError().text()));
        optra()->db()->db().rollback();
        return false;
    }

    int appid = query.lastInsertId().toInt();

    query.exec(QString("DELETE FROM applicationsdep WHERE sid=%1").arg(appid));
    if (!aoi.requiredZones().isEmpty() && !setDBDependency(aoi.requiredZones(), appid, "Required")){
        if (QMessageBox::question(0, "Install Application", "Dependency missing install anyway(application will be disabled) ?", QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::No){
            optra()->db()->db().rollback();
            optra()->warning("Application install aborted");
            return false;
        }
    }

    if (!aoi.functionZones().isEmpty() && !setDBDependency(aoi.functionZones(), appid, "Function")){
        optra()->info("Function zone(s) missing, some funcionality can be disabled, until you install required zone(s)");
    }

    if (!aoi.futureZones().isEmpty() && !setDBDependency(aoi.futureZones(), appid, "Future")){
        optra()->info("Future zone(s) missing, extra funcionality can be disabled, until you install required zone(s)");
    }

    optra()->db()->db().commit();
    return true;
}
